/* eslint-disable */
import Camera from './Camera';
import Movie from './Movie';
import TreeMirror from './TreeMirror';

const $ = {
  fadeOut(element, callback) {
    var op = 1;  // initial opacity
    var timer = setInterval(function () {
      if (op <= 0.1){
        clearInterval(timer);
        element.style.display = 'none';
        callback();
      }
      element.style.opacity = op;
      element.style.filter = 'alpha(opacity=' + op * 100 + ")";
      op -= op * 0.1;
    }, 50);
  },
  fadeIn(element, callback) {
    var op = 0.1;  // initial opacity
    element.style.display = 'block';
    var timer = setInterval(function () {
      if (op >= 1){
        clearInterval(timer);
        callback();
      }
      element.style.opacity = op;
      element.style.filter = 'alpha(opacity=' + op * 100 + ")";
      op += op * 0.1;
  }, 10);
  },
};


/**
 * Player 用户行为回放播放器
 * @extends Camera
 */
export default class Player extends Camera {

  /**
   * 创建player
   * @param {Object} opts player选项
   * @param {HTMLElement} [opts.target] 准备回放用户行为的html element
   * @param {Object} [opts.onEvents={}] 自定义事件处理器(一般不需要自定义)
   * @example
   * let player = new cimice.Player({
   *     target: document.getElementById('to-play'),
   *     onEvents: {
   *       'scroll': (player, frame) => {
   *         console.log('Override default scroll event callback');
   *        }
   *     }
   * });
   */
  constructor(opts = {}) {
    super(opts);
    if (opts.target) {
      this.setTarget(opts.target);
    }
    this.movie = null;
    this.currentFrame = null;
    this.displayer = null;
    this.isPlaying = false;
    this.progress = 0;
    this.speed = 1;
    this.timerId = null;
    this.initCursor();

    /**
     * 触发开始播放事件
     *
     * @event Player#play
     */
    this.on('play', () => {
      this.isPlaying = true;
    });

    /**
     * 触发停止播放事件
     *
     * @event Player#stop
     */
    this.on('stop', () => {
      this.isPlaying = false;
      this.currentFrame = null;
      clearTimeout(this.timerId);
      this.progress = 0;
    });

    /**
     * 触发播放事件(回掉函数中可以获取到当前frame的信息)
     *
     * @event Player#playing
     * @property {Frame} frame Frame playing
     */
    this.on('playing', (frame) => {}); // eslint-disable-line no-unused-vars

    /**
     * 触发暂停播放事件
     *
     * @event Player#pause
     */
    this.on('pause', () => {
      this.isPlaying = false;
    });

    /**
     * 触发继续播放事件
     *
     * @event Player#continue
     */
    this.on('continue', () => {
      this.isPlaying = true;
    });

    /**
     * 触发进度条改变事件
     *
     * @event Play#progress
     */
    this.on('progress', data => {});
  }

  setMovie(movie) {
    if (!(movie instanceof Movie)) {
      throw new Error('Target is not an instance of HTMLElement');
    }
    this.movie = movie;
    return this;
  }

  /**
   * 重新设定当前播放的frame(用于进度条拖动到指定位置播放)
   * @param  {Frame} newframe 指定的frame
   * @return {this}
   */
  resetCurrentFrame(newframe) {
    this.stop();

    /* reset player and play to specied frame quickly */

    // reset player
    const target = this.target;
    while (target.firstChild) {
      target.removeChild(target.firstChild);
    }

    if (!this.displayer) {
      this.maskBox = document.createElement('div');
      this.displayer = document.createElement('iframe');
      this.tryGetTarget().appendChild(this.maskBox);
      this.tryGetTarget().appendChild(this.displayer);
      // this.displayer.style = 'margin-left: 0px; display: none;';
      this.displayer.setAttribute('style', 'margin-left: 0px; display: none;')
      this.displayer.sandbox = 'allow-forms allow-scripts allow-same-origin';
      this.displayer.width = '1676';
      this.displayer.height = '886';
      this.displayer.frameborder = '0';
      this.displayer.border = '0';
      this.displayer['data-origwidth'] = '1676';
      this.displayer['data-origheight'] = '886';
      this.displayer.scrolling = 'no';
      this.maskBox.setAttribute('class', 'mask-box');
    }
    const displayerWindow = this.displayer.contentWindow.document;
    while (displayerWindow.firstChild) {
      displayerWindow.removeChild(displayerWindow.firstChild);
    }
    this.target = displayerWindow;

    const player = this;
    this.mirror = new TreeMirror(this.displayer.contentWindow.document, {
      createElement: function(tagName) {
        if (tagName == 'SCRIPT') {
          var node = document.createElement('NO-SCRIPT');
          node.style.display = 'none';
          return node;
        }

        if (tagName == 'HEAD') {
          var node = document.createElement('HEAD');
          node.appendChild(document.createElement('BASE'));
          node.firstChild.href = player.movie.base;
          return node;
        }
      }
    });

    // play to specied frame quickly
    const movie = this.tryGetMovie();
    this.mirror.initialize(movie.getRootId(), movie.getChildren());
    this.target.body.appendChild(this.cursor);
    //this.playFastTo(movie.frames[0], newframe);
    this.playFromDrag(newframe);
    // this.displayer.style = 'margin-left: 0px; display: block;';
    this.displayer.setAttribute('style', 'margin-left: 0px; display: block;');

    return this;
  }

  getFrameByProgress(progress) {
    const movie = this.tryGetMovie();
    if (progress >= 1) {
      return movie.frames[movie.frames.length - 1];
    }
    return movie.frames[Math.ceil(movie.frames.length * progress) - 1];
  }

  /**
   * 设置播放速度
   * @param {speed} speed 播放速度(1=正常播放，2=2倍速度播放，3=3倍速度播放)
   * @return {this}
   */
  setSpeed(speed) {
    this.speed = speed;
    return this;
  }

  /**
   * 开始播放
   * @returns {this}
   */
  play() {
    if (this.isPlaying) {
      return this;
    }

    const movie = this.tryGetMovie();
    this.target.body.appendChild(this.cursor);

    const frames = movie.frames;
    this.emit('play');
    if (frames.length) {
      this.target.body.scrollTop = 0
      this.target.body.scrollLeft = 0
      this.playFrom(frames[0]);
    } else {
      this.stop();
    }

    return this;
  }

  /**
   * 停止播放
   * @returns {this}
   */
  stop() {
    this.emit('stop');
    return this;
  }

  /**
   * 继续播放
   * @return {this}
   */
  continue() {
    if (this.isPlaying) {
      return this;
    }

    const movie = this.tryGetMovie();
    const frames = this.movie.frames;
    this.emit('continue');
    if (frames.length) {
      this.playFrom(this.currentFrame);
    } else {
      this.stop();
    }

    return this;

  }

  /**
   * 暂停播放
   * @return {this}
   */
  pause() {
    this.emit('pause');
    return this;
  }

  /***
   * 返回鼠标指针
   * @returns {HTMLElement}
   */
  getCursor() {
    return this.cursor;
  }

  /***
   * 获取鼠标指针x轴位置(单位: px)
   * @param {Frame|null} frame 传入frame，返回指定frame的鼠标x轴位置，不传，返回当前播放frame的
   * @returns {number}
   */
  getCursorX(frame) {
    const f = frame || this.currentFrame;
    const tr = this.tryGetTarget();
    const mLeft = this.tryGetMovie().left;
    const scrollLeft = mLeft === 0 ? f.scrollX : tr.scrollLeft;
    const x = (f.cursorX - mLeft) + scrollLeft;
    return x;
  }

  /***
   * 获取鼠标指针y轴位置(单位: px)
   * @param {Frame|null} frame 传入frame，返回指定frame的鼠标y轴位置，不传，返回当前播放frame的
   * @returns {number}
   */
  getCursorY(frame) {
    const f = frame || this.currentFrame;
    const tr = this.tryGetTarget();
    const mTop = this.tryGetMovie().top;
    const scrollTop = mTop === 0 ? f.scrollY : tr.scrollTop;
    const y = (f.cursorY - mTop) + scrollTop;
    return y;
  }

  /**
   * 获取当前播放movie，如果movie为空，会抛出异常
   * @returns {Movie} movie 当前播放的movie
   * @private
   */
  tryGetMovie() {
    const movie = this.movie;

    if (!movie) {
      throw new Error('Movie not found');
    }

    return movie;
  }

  /***
   * 刷新播放器页面
   * @return {[type]} [description]
   */
  refreshScene() {
    const displayerWindow = this.displayer.contentWindow.document;
    displayerWindow.body.innerHTML = this.movie.getDecodedScene();
  }

  /***
   * 创建并返回一个鼠标对象
   * @return {HTMLElement} HTMLElement cursor element
   * @private
   */
  initCursor() {
    const cursor = document.createElement('div');
    cursor.id = 'cursor';
    cursor.style.position = 'fixed';
    cursor.style.zIndex = '9999';
    cursor.style.width = '11px';
    cursor.style.height = '18px';
    // eslint-disable-next-line quotes
    cursor.innerHTML = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 934' style='height:100%;width:100%'><path d='M0 0l512 510.2 -201.8 32.9L480.4 852.1l-153.9 81.9L154.2 622.1 0 776.3V0z' style='fill:#000;stroke-width:3em;stroke:#fff;'/></svg>`;
    this.cursor = cursor;
    return this.cursor;
  }

  /**
   * 初始化播放器
   * @param {Movie} movie 准备播放的movie
   * @returns {this}
   * @private
   */
  initPlayer(movie) {
    this.movie = movie;

    const target = this.tryGetTarget();
    while (target.firstChild) {
      target.removeChild(target.firstChild);
    }

    target.style.position = 'relative';

    if (!this.displayer) {
      this.maskBox = document.createElement('div');
      this.displayer = document.createElement('iframe');
      this.tryGetTarget().appendChild(this.displayer);
      this.tryGetTarget().appendChild(this.maskBox);
      // this.displayer.style = 'margin-left: 0px; display: block; ::-webkit-scrollbar { display:none }';
      this.displayer.setAttribute('style', 'margin-left: 0px; display: block; ::-webkit-scrollbar { display:none }')
      this.displayer.sandbox = 'allow-forms allow-scripts allow-same-origin';
      this.displayer.width = '1676';
      this.displayer.height = '886';
      this.displayer['data-origwidth'] = '1676';
      this.displayer['data-origheight'] = '886';
      this.displayer.scrolling = 'no';
      this.maskBox.setAttribute('class', 'mask-box');
    }
    const displayerWindow = this.displayer.contentWindow.document;

    while (displayerWindow.firstChild) {
      displayerWindow.removeChild(displayerWindow.firstChild);
    }

    this.target = displayerWindow;
    // this.target.scrollTop = movie.scrollY;
    // this.target.scrollLeft = movie.scrollX;

    const player = this;
    this.mirror = new TreeMirror(this.displayer.contentWindow.document, {
      createElement: function(tagName) {
        if (tagName == 'SCRIPT') {
          var node = document.createElement('NO-SCRIPT');
          node.style.display = 'none';
          return node;
        }

        if (tagName == 'HEAD') {
          var node = document.createElement('HEAD');
          node.appendChild(document.createElement('BASE'));
          node.firstChild.href = player.movie.base;
          return node;
        }
      }
    });
    this.mirror.initialize(movie.getRootId(), movie.getChildren());
    var head = this.displayer.contentWindow.document.head.outerHTML;
    var body = this.displayer.contentWindow.document.body.outerHTML;
    var doctype = this.mirror.idMap[1];
    if (this.displayer.contentWindow.document.baseURI !== 'https://www.7moor.com/') {
      this.displayer.contentWindow.document.open();
      this.displayer.contentWindow.document.write(`<!DOCTYPE html><html>${head}${body}</html>`);
      this.displayer.contentWindow.document.close();

      this.mirror.root = this.displayer.contentWindow.document;
      this.mirror.idMap = {};
      while (displayerWindow.firstChild) {
        displayerWindow.removeChild(displayerWindow.firstChild);
      }

      this.mirror = new TreeMirror(this.displayer.contentWindow.document, {
        createElement: function(tagName) {
          if (tagName == 'SCRIPT') {
            var node = document.createElement('NO-SCRIPT');
            node.style.display = 'none';
            return node;
          }

          if (tagName == 'HEAD') {
            var node = document.createElement('HEAD');
            node.appendChild(document.createElement('BASE'));
            node.firstChild.href = player.movie.base;
            return node;
          }
        }
      });
      this.mirror.initialize(movie.getRootId(), movie.getChildren());
    }


    return this.displayer;
  }

  /***
   * 从指定frame开始播放
   * @param {Frame} 指定的frame
   * @private
   */
  playFrom(frame) {
    if (this.isPlaying) {
      const movie = this.tryGetMovie();
      const index = movie.frames.indexOf(frame);
      const nextFrame = movie.frames[index + 1];
      const event = frame.eventType;
      const callback = this.onEvents[event] || this.abstractOnEvent;
      // this.progress = ((index + 1) / movie.frames.length).toFixed(2);
      this.emit('progress', { progress: index });
      this.currentFrame = frame;
      this.emit('playing', frame);
      this.emit(frame.eventType, frame);
      callback(this, frame);
      if (nextFrame) {
        const wait = nextFrame.ts - frame.ts;
        let waitTime = wait > 50 ? 10 : wait;
        this.timerId = setTimeout(() => {
          this.playFrom(nextFrame);
        }, waitTime / this.speed);
      } else {
        this.stop();
      }
    }
  }

  /***
   * 快速从frame播放到finalFrame(过程不可见，用于滚动条拖动)
   * @param  {Frame} frame      开始frame一般是第一个frame
   * @param  {Frame} finalFrame 拖动到的frame
   * @return {null}
   */
  playFastTo(frame, finalFrame) {
    if (frame.ts !== finalFrame.ts) {
      const movie = this.tryGetMovie();
      const index = movie.frames.indexOf(frame);
      const nextFrame = movie.frames[index + 1];
      const event = frame.eventType;
      const callback = this.onEvents[event] || this.abstractOnEvent;
      // this.emit('progress', { progress: index });
      this.currentFrame = frame;
      this.emit(frame.eventType, frame);
      callback(this, frame);
      if (nextFrame) {
        this.playFastTo(nextFrame, finalFrame);
      } else {
        console.error('dragged to unkown frame');
      }
    } else {
      this.currentFrame = frame;
    }
  }

  playFromDrag(finalFrame) {
    this.target.body.scrollTop = 0
    this.target.body.scrollLeft = 0
    const movie = this.tryGetMovie()
    const length = movie.frames.indexOf(finalFrame)
    for (let i = 0; i < movie.frames.length; i++) {
      let frame = movie.frames[i]
      if (frame.ts !== finalFrame.ts) {
        const event = frame.eventType
        const callback = this.onEvents[event] || this.abstractOnEvent
        this.currentFrame = frame
        this.emit(frame.eventType, frame)
        callback(this, frame)
        // console.log(i)
      } else {
        this.currentFrame = frame
        break
      }
    }
  }

  /**
   * 通用事件回掉函数
   * @param {Player} player 播放器
   * @param {Frame} frame 触发事件的frame
   * @private
   */
  // eslint-disable-next-line class-methods-use-this
  abstractOnEvent(player, frame) {
    // console.dir(frame);
    const target = player.target;
    const movie = player.movie;
    const u = 'px';

    const scrollTop = movie.top === 0 ? frame.scrollY : target.documentElement.scrollTop + target.body.scrollTop;
    const scrollLeft = movie.left === 0 ? frame.scrollX : target.documentElement.scrollLeft + target.body.scrollTop;

    if (frame.eventType === 'scroll') {
      target.documentElement.scrollTop = frame.scrollY;
      target.body.scrollTop = frame.scrollY;
      target.documentElement.scrollLeft = frame.scrollX;
      target.body.scrollLeft = frame.scrollX;
    }
    if (frame.width) {
      //target.style.width = frame.width + u;
      player.displayer.style.width = frame.width + u;
      if (frame.width > document.body.clientWidth) {
        player.maskBox.style.transform = `scaleY(${(document.body.clientWidth - 6) / frame.width})`
        player.displayer.style.transform = `scale(${(document.body.clientWidth - 6) / frame.width})`
      }
    }
    if (frame.height) {
      //target.style.height = `${frame.height}px`;
      player.displayer.style.height = frame.height + u;
      player.displayer['data-origheight'] = frame.height;
    }

    const px = (frame.cursorX - movie.left) + scrollLeft;
    const py = (frame.cursorY - movie.top) + scrollTop;

    player.cursor.style.left = px + u;
    player.cursor.style.top = py + u;
  }


  /**
   * 改变frame的宽高
   * @private
   */
  changeFrame(player, frame) {
    const target = player.target.body;
    const u = 'px';
    if (frame.width) {
      //target.style.width = frame.width + u;
      player.displayer.style.width = frame.width + u;
      if (frame.width > document.body.clientWidth) {
        player.maskBox.style.transform = `scaleY(${(document.body.clientWidth - 6) / frame.width})`
        player.displayer.style.transform = `scale(${(document.body.clientWidth - 6) / frame.width})`
      }
    }
    if (frame.height) {
      //target.style.height = `${frame.height}px`;
      player.displayer.style.height = frame.height + u;
      player.displayer['data-origheight'] = frame.height;
    }
  }


  /**
   * 默认的事件触发器
   * @return {Objet}
   * @private
   */
  // eslint-disable-next-line class-methods-use-this
  onEvents() {
    return {
      click: (player, frame) => {
        const target = player.target.body;
        const scrollTop = player.movie.top === 0 ? frame.scrollY : target.scrollTop;
        const scrollLeft = player.movie.left === 0 ? frame.scrollX : target.scrollLeft;
        const dot = document.createElement('div');
        dot.className = 'dotClick';

        this.changeFrame(player, frame)
        // const px = (frame.cursorX - player.movie.left) + scrollLeft;
        // const py = (frame.cursorY - player.movie.top) + scrollTop;

        let px = frame.cursorX;
        let py = frame.cursorY;
        dot.style.backgroundColor = '#f04242';
        dot.style.width = '10px';
        dot.style.height = '10px';
        dot.style.borderRadius = '100%';
        dot.style.boxShadow = '0 0 3px #d92b2b';
        dot.style.marginLeft = '-5px';
        dot.style.marginTop = '-5px';
        dot.style.position = 'absolute';
        dot.style.left = `${px}px`;
        dot.style.top = `${py}px`;
        dot.style.zIndex = '9998'
        target.appendChild(dot);

        setTimeout(() => {
          $.fadeOut(dot, () => target.removeChild(dot));
        }, 2000);
      },
      mutation: (player, frame) => {
        this.changeFrame(player, frame)
        const changed = frame.changed;
        const mirror = player.mirror;
        try {
          mirror.applyChanged.apply(mirror,changed);
        } catch (e) {

        }
      },
      scroll: (player, frame) => {
        this.changeFrame(player, frame)
        const target = player.target;
        const movie = player.movie;
        const u = 'px';

        const scrollTop = movie.top === 0 ? frame.scrollY : target.scrollTop;
        const scrollLeft = movie.left === 0 ? frame.scrollX : target.scrollLeft;
        if (frame.eventType === 'scroll') {
          target.documentElement.scrollTop = frame.scrollY;
          target.body.scrollTop = frame.scrollY;
          target.documentElement.scrollLeft = frame.scrollX;
          target.body.scrollLeft = frame.scrollX;
        }

        // const px = (frame.cursorX - movie.left) + scrollLeft;
        // const py = (frame.cursorY - movie.top) + scrollTop;
        // player.cursor.style.left = px + u;
        // player.cursor.style.top = py + u;

        this.changeFrame(player, frame)
        if (frame.element) {
          const scrollElement = this.mirror.deserializeNode(frame.element);
          scrollElement.scrollTop = frame.elementScrollY;
          scrollElement.scrollLeft = frame.elementScrollX;
        }
      },
      keyup: (player, frame) => {
        this.changeFrame(player, frame)
        if (frame.element) {
          const inputElement = this.mirror.deserializeNode(frame.element);
          if (inputElement.tagName == 'TEXTAREA') {
            inputElement.innerHTML = frame.elementTextValue;
          } else {
            inputElement.setAttribute('value', frame.elementTextValue);
          }
        }
      },
      mousemove: (player, frame) => {
        this.changeFrame(player, frame)
        const target = player.target.body;
        const movie = player.movie;
        const u = 'px';

        const scrollTop = movie.top === 0 ? frame.scrollY : target.scrollTop;
        const scrollLeft = movie.left === 0 ? frame.scrollX : target.scrollLeft;
        // let px = (frame.cursorX - movie.left) + scrollLeft;
        // let py = (frame.cursorY - movie.top) + scrollTop;
        let px = frame.cursorX - scrollLeft;
        let py = frame.cursorY - scrollTop;

        player.cursor.style.left = px + u;
        player.cursor.style.top = py + u;
      },
      change: (player, frame) => {
        this.changeFrame(player, frame)
        const target = player.target.body;
        const movie = player.movie;
        const srcElement = frame.element;
        if (srcElement) {
          const element = this.mirror.deserializeNode(frame.element);
          if (element.tagName == 'SELECT') {
            element.selectedIndex = parseInt(frame.elementTextValue, 10);
          } else if (element.tagName == 'INPUT' && element.type == 'radio') {
            element.checked = true;
          } else if (element.tagName == 'INPUT' && element.type == 'checkbox') {
            element.checked = frame.elementTextValue;
          }
        }
      }
    };
  }
}
